#include "plugin_manager.h"
#include "include/plugin_render.h"
#include "player.h"
#include "recorder.h"

#undef LOG_TAG
#define LOG_TAG "PLUGINRENDER"

namespace NativeXcomponentSample {
namespace {
constexpr uint32_t LOG_PRINT_DOMAIN = 0xFF00;

void OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window) {
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceCreatedCB");
    if ((component == nullptr) || (window == nullptr)) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
                     "OnSurfaceCreatedCB: component or window is null");
        return;
    }

    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
                     "OnSurfaceCreatedCB: Unable to get XComponent id");
        return;
    }

    std::string id(idStr);
    auto render = PluginRender::GetInstance(id);
    uint64_t width;
    uint64_t height;
    int32_t xSize = OH_NativeXComponent_GetXComponentSize(component, window, &width, &height);
    if ((xSize == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) && (render != nullptr)) {
        render->UpdateNativeWindow(window, width, height);
    }
}

void OnSurfaceChangedCB(OH_NativeXComponent *component, void *window) {
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceChangedCB");
    if ((component == nullptr) || (window == nullptr)) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
                     "OnSurfaceChangedCB: component or window is null");
        return;
    }

    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
                     "OnSurfaceChangedCB: Unable to get XComponent id");
        return;
    }

    std::string id(idStr);
    auto render = PluginRender::GetInstance(id);
    if (render != nullptr) {
        render->OnSurfaceChanged(component, window);
        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "surface changed");
    }
}

void OnSurfaceDestroyedCB(OH_NativeXComponent *component, void *window) {
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceDestroyedCB");
    if ((component == nullptr) || (window == nullptr)) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
                     "OnSurfaceDestroyedCB: component or window is null");
        return;
    }

    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
                     "OnSurfaceDestroyedCB: Unable to get XComponent id");
        return;
    }

    std::string id(idStr);
    PluginRender::Release(id);
}

void DispatchTouchEventCB(OH_NativeXComponent *component, void *window) {
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "DispatchTouchEventCB");
    if ((component == nullptr) || (window == nullptr)) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
                     "DispatchTouchEventCB: component or window is null");
        return;
    }

    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
                     "DispatchTouchEventCB: Unable to get XComponent id");
        return;
    }

    std::string id(idStr);
    PluginRender *render = PluginRender::GetInstance(id);
    if (render != nullptr) {
        render->OnTouchEvent(component, window);
    }
}

struct AsyncCallbackInfo {
    napi_env env;
    napi_async_work asyncWork;
    napi_deferred deferred;
    int32_t resultCode = 0;
    std::string resultStr = "";
    std::string surfaceId = "";
    SampleInfo sampleInfo;
};

void DealCallBack(napi_env env, void *data) {
    AsyncCallbackInfo *asyncCallbackInfo = static_cast<AsyncCallbackInfo *>(data);
    napi_value videoWidth;
    napi_create_int32(env, asyncCallbackInfo->sampleInfo.videoWidth, &videoWidth);
    napi_value videoHeight;
    napi_create_int32(env, asyncCallbackInfo->sampleInfo.videoHeight, &videoHeight);
    napi_value durationTime;
    napi_create_int64(env, asyncCallbackInfo->sampleInfo.durationTime, &durationTime);
    napi_value code;
    napi_create_int32(env, asyncCallbackInfo->resultCode, &code);
    napi_value message;
    napi_create_string_utf8(env, asyncCallbackInfo->resultStr.data(), NAPI_AUTO_LENGTH, &message);
    napi_value surfaceId;
    napi_create_string_utf8(env, asyncCallbackInfo->surfaceId.data(), NAPI_AUTO_LENGTH, &surfaceId);
    napi_value obj;
    napi_create_object(env, &obj);

    napi_set_named_property(env, obj, "code", code);
    napi_set_named_property(env, obj, "surfaceId", surfaceId);
    napi_set_named_property(env, obj, "videoWidth", videoWidth);
    napi_set_named_property(env, obj, "videoHeight", videoHeight);
    napi_set_named_property(env, obj, "durationTime", durationTime);
    napi_set_named_property(env, obj, "message", message);
    napi_resolve_deferred(asyncCallbackInfo->env, asyncCallbackInfo->deferred, obj);
    napi_delete_async_work(env, asyncCallbackInfo->asyncWork);
    delete asyncCallbackInfo;
}

void SetCallBackResult(AsyncCallbackInfo *asyncCallbackInfo, int32_t code, std::string str) {
    asyncCallbackInfo->resultCode = code;
    asyncCallbackInfo->resultStr = str;
}

void SurfaceIdCallBack(AsyncCallbackInfo *asyncCallbackInfo, std::string surfaceId) {
    asyncCallbackInfo->surfaceId = surfaceId;
}

void VideoInfoCallback(AsyncCallbackInfo *asyncCallbackInfo, SampleInfo sampleInfo) {
    asyncCallbackInfo->sampleInfo.videoWidth = sampleInfo.videoWidth;
    asyncCallbackInfo->sampleInfo.videoHeight = sampleInfo.videoHeight;
    asyncCallbackInfo->sampleInfo.durationTime = sampleInfo.durationTime;
}

void InitializePlayer(napi_env env, void *data) {
    AsyncCallbackInfo *asyncCallbackInfo = static_cast<AsyncCallbackInfo *>(data);
    int32_t ret = Player::GetInstance().Init(asyncCallbackInfo->sampleInfo);
    if (ret != AVCODEC_SAMPLE_ERR_OK) {
        SetCallBackResult(asyncCallbackInfo, -1, "Player init failed");
    } else {
        AVCODEC_SAMPLE_LOGI("Video width is %{public}i, height is %{public}i", asyncCallbackInfo->sampleInfo.videoWidth,
                            asyncCallbackInfo->sampleInfo.videoHeight);
        VideoInfoCallback(asyncCallbackInfo, asyncCallbackInfo->sampleInfo);
    }
}

void Play(napi_env env, void *data) {
    AsyncCallbackInfo *asyncCallbackInfo = static_cast<AsyncCallbackInfo *>(data);
    int32_t ret = Player::GetInstance().Start();
    ret = Player::GetInstance().WaitForDone();
    if (ret != AVCODEC_SAMPLE_ERR_OK) {
        SetCallBackResult(asyncCallbackInfo, -1, "Player start failed");
    } else {
        SetCallBackResult(asyncCallbackInfo, 0, "Player start finish");
    }
}

void NativeStop(napi_env env, void *data) {
    AsyncCallbackInfo *asyncCallbackInfo = static_cast<AsyncCallbackInfo *>(data);
    int32_t ret = Recorder::GetInstance().Stop();
    if (ret != AVCODEC_SAMPLE_ERR_OK) {
        SetCallBackResult(asyncCallbackInfo, -1, "Recorder stop failed");
    }
    SetCallBackResult(asyncCallbackInfo, 0, "Recorder stop failed");
}
} // namespace

std::unordered_map<std::string, PluginRender *> PluginRender::instance_;
int32_t PluginRender::hasDraw_ = 0;
int32_t PluginRender::hasChangeColor_ = 0;

PluginRender::PluginRender(std::string &id) : renderThread_(std::make_unique<NativeXcomponentSample::RenderThread>()) {
    this->id_ = id;
}

PluginRender *PluginRender::GetInstance(std::string &id) {
    if (instance_.find(id) == instance_.end()) {
        PluginRender *instance = new PluginRender(id);
        instance_[id] = instance;
        return instance;
    } else {
        return instance_[id];
    }
}

void PluginRender::Export(napi_env env, napi_value exports) {
    if ((env == nullptr) || (exports == nullptr)) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "Export: env or exports is null");
        return;
    }

    napi_property_descriptor desc[] = {
        {"StartCamera", nullptr, PluginRender::StartCamera, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"StopCamera", nullptr, PluginRender::StopCamera, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"InitPlayer", nullptr, PluginRender::InitPlayer, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"StartPlay", nullptr, PluginRender::StartPlay, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"PausePlay", nullptr, PluginRender::PausePlay, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"ResumePlay", nullptr, PluginRender::ResumePlay, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"LoopPlay", nullptr, PluginRender::LoopPlay, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"StopPlay", nullptr, PluginRender::StopPlay, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"Seek", nullptr, PluginRender::Seek, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"AddBW", nullptr, PluginRender::AddBW, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"InitRecorder", nullptr, PluginRender::InitRecorder, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"StartRecord", nullptr, PluginRender::StartRecord, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"StopRecord", nullptr, PluginRender::StopRecord, nullptr, nullptr, nullptr, napi_default, nullptr},
    };
    if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "Export: napi_define_properties failed");
    }
}

void PluginRender::Release(std::string &id) {
    PluginRender *render = PluginRender::GetInstance(id);
    if (render != nullptr) {
        Recorder::GetInstance().Stop();
        render->StopPlay();

        instance_.erase(instance_.find(id));
        delete render;
        render = nullptr;
    }
}

PluginRender *PluginRender::GetPluginRender(napi_env env, napi_callback_info info) {
    napi_value thisArg;
    if (napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr) != napi_ok) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "NapiDrawPattern: napi_get_cb_info fail");
        return nullptr;
    }
    // 获取环境变量中Xcomponent实例
    napi_value exportInstance;
    if (napi_get_named_property(env, thisArg, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender",
                     "NapiDrawPattern: napi_get_named_property fail");
        return nullptr;
    }

    OH_NativeXComponent *nativeXComponent = nullptr;
    if (napi_unwrap(env, exportInstance, reinterpret_cast<void **>(&nativeXComponent)) != napi_ok) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "NapiDrawPattern: napi_unwrap fail");
        return nullptr;
    }
    // 获取XComponent实例id
    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
    if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender",
                     "NapiDrawPattern: Unable to get XComponent id");
        return nullptr;
    }

    std::string id(idStr);
    PluginRender *render = PluginRender::GetInstance(id);
    return render;
}

napi_value PluginRender::StartCamera(napi_env env, napi_callback_info info) {
    PluginRender *render = GetPluginRender(env, info);
    if (render != nullptr) {
        render->CreateCamera();
        render->renderThread_->SetRenderAngle(true);
    }
    return nullptr;
}

napi_value PluginRender::StopCamera(napi_env env, napi_callback_info info) {
    PluginRender *render = GetPluginRender(env, info);
    if (render != nullptr) {
        render->StopCamera();
        render->renderThread_->SetRenderAngle(false);
    }
    return nullptr;
}

void PluginRender::CreateCamera() {
    std::string surfaceIdStr = std::to_string(renderThread_->GetNativeImageSurfaceId());
    ndkCamera_ = std::make_unique<OHOS_CAMERA_SAMPLE::NDKCamera>(surfaceIdStr.c_str(), 2, 0);
}

void PluginRender::StopCamera() { ndkCamera_->ReleaseCamera(); }

napi_value PluginRender::StartRecord(napi_env env, napi_callback_info info) {
    PluginRender *render = GetPluginRender(env, info);
    if (render != nullptr) {
        int32_t ret = Recorder::GetInstance().Start();
        napi_value result = nullptr;
        napi_create_int32(env, ret, &result);
        return result;
    }
    return nullptr;
}

napi_value PluginRender::StopRecord(napi_env env, napi_callback_info info) {
    PluginRender *render = GetPluginRender(env, info);
    if (render != nullptr) {
        napi_value promise;
        napi_deferred deferred;
        napi_create_promise(env, &deferred, &promise);

        AsyncCallbackInfo *asyncCallbackInfo = new AsyncCallbackInfo();

        asyncCallbackInfo->env = env;
        asyncCallbackInfo->asyncWork = nullptr;
        asyncCallbackInfo->deferred = deferred;

        napi_value resourceName;
        napi_create_string_latin1(env, "recorder", NAPI_AUTO_LENGTH, &resourceName);
        napi_create_async_work(
            env, nullptr, resourceName, [](napi_env env, void *data) { NativeStop(env, data); },
            [](napi_env env, napi_status status, void *data) { DealCallBack(env, data); }, (void *)asyncCallbackInfo,
            &asyncCallbackInfo->asyncWork);
        napi_queue_async_work(env, asyncCallbackInfo->asyncWork);
        return promise;
    }
    return nullptr;
}

napi_value PluginRender::StartPlay(napi_env env, napi_callback_info info) {
    PluginRender *render = GetPluginRender(env, info);
    if (render != nullptr) {
        if (render->renderThread_->GetNativeImageWindow() != nullptr) {
            CHECK_AND_RETURN_RET_LOG(env != nullptr && info != nullptr, nullptr, "env or info is null");
            napi_deferred deferred;
            napi_value promise;
            CHECK_AND_RETURN_RET_LOG(napi_ok == napi_create_promise(env, &deferred, &promise), nullptr,
                                     "Start: Create promise failed");
            AsyncCallbackInfo *asyncCallbackInfo = new AsyncCallbackInfo();
            asyncCallbackInfo->env = env;
            asyncCallbackInfo->asyncWork = nullptr;
            asyncCallbackInfo->deferred = deferred;
            napi_value resourceName;
            CHECK_AND_RETURN_RET_LOG(napi_ok ==
                                         napi_create_string_latin1(env, "Start", NAPI_AUTO_LENGTH, &resourceName),
                                     nullptr, "Start: Create resourceName failed");
            CHECK_AND_RETURN_RET_LOG(
                napi_ok == napi_create_async_work(
                               env, nullptr, resourceName, [](napi_env env, void *data) { Play(env, data); },
                               [](napi_env env, napi_status status, void *data) { DealCallBack(env, data); },
                               (void *)asyncCallbackInfo, &asyncCallbackInfo->asyncWork),
                nullptr, "Initialize: Create async work failed");
            CHECK_AND_RETURN_RET_LOG(napi_ok == napi_queue_async_work(env, asyncCallbackInfo->asyncWork), nullptr,
                                     "Start: queue_async_work failed");
            return promise;
        }
    }
    return nullptr;
}

void PluginRender::StopPlay() { Player::GetInstance().Kill(); }

void PluginRender::DeleteEncoderSurface(void) { renderThread_->DeleteEncoderSurface(); }

napi_value PluginRender::StopPlay(napi_env env, napi_callback_info info) {
    PluginRender *render = GetPluginRender(env, info);
    if (render != nullptr) {
        render->DeleteEncoderSurface();
        render->StopPlay();
        render->renderThread_->SetRenderAngle(true);
    }
    return nullptr;
}

void PluginRender::CreateEncoderSurface(OHNativeWindow *window, int32_t width, int32_t height) {
    renderThread_->CreateEncoderSurface(window, width, height);
}

napi_value PluginRender::InitRecorder(napi_env env, napi_callback_info info) {
    CHECK_AND_RETURN_RET_LOG(env != nullptr && info != nullptr, nullptr, "env or info is null");
    PluginRender *render = GetPluginRender(env, info);
    if (render != nullptr) {
        size_t argc = 7;
        napi_value args[7] = {nullptr};
        CHECK_AND_RETURN_RET_LOG(napi_ok == napi_get_cb_info(env, info, &argc, args, nullptr, nullptr), nullptr,
                                 "GetContext napi_get_cb_info failed");
        SampleInfo sampleInfo;
        int index = 0;
        CHECK_AND_RETURN_RET_LOG(napi_ok == napi_get_value_int32(env, args[index++], &sampleInfo.outputFd), nullptr,
                                 "InitRecorder: Get value failed");
        char videoCodecMime[20] = {0};
        size_t videoCodecMimeStrlen = 0;
        size_t len = 20;
        CHECK_AND_RETURN_RET_LOG(
            napi_ok == napi_get_value_string_utf8(env, args[index++], videoCodecMime, len, &videoCodecMimeStrlen),
            nullptr, "InitRecorder: Get value failed");
        CHECK_AND_RETURN_RET_LOG(napi_ok == napi_get_value_int32(env, args[index++], &sampleInfo.videoWidth), nullptr,
                                 "InitRecorder: Get value failed");
        CHECK_AND_RETURN_RET_LOG(napi_ok == napi_get_value_int32(env, args[index++], &sampleInfo.videoHeight), nullptr,
                                 "InitRecorder: Get value failed");
        CHECK_AND_RETURN_RET_LOG(napi_ok == napi_get_value_double(env, args[index++], &sampleInfo.frameRate), nullptr,
                                 "InitRecorder: Get value failed");
        CHECK_AND_RETURN_RET_LOG(napi_ok == napi_get_value_int32(env, args[index++], &sampleInfo.isHDRVivid), nullptr,
                                 "InitRecorder: Get value failed");
        CHECK_AND_RETURN_RET_LOG(napi_ok == napi_get_value_int64(env, args[index++], &sampleInfo.bitrate), nullptr,
                                 "InitRecorder: Get value failed");
        sampleInfo.videoCodecMime = videoCodecMime;
        if (sampleInfo.isHDRVivid) {
            sampleInfo.hevcProfile = HEVC_PROFILE_MAIN_10;
        }
        int32_t ret = Recorder::GetInstance().Init(sampleInfo);
        OHNativeWindow *nativeWindow = sampleInfo.window;
        CHECK_AND_RETURN_RET_LOG(nativeWindow != nullptr, nullptr, "InitRecorder: nativeWindow is nullptr");
        render->CreateEncoderSurface(nativeWindow, sampleInfo.videoWidth, sampleInfo.videoHeight);
        napi_value result = nullptr;
        napi_create_int32(env, ret, &result);
        return result;
    }
    return nullptr;
}

napi_value PluginRender::PausePlay(napi_env env, napi_callback_info info) {
    CHECK_AND_RETURN_RET_LOG(env != nullptr && info != nullptr, nullptr, "env or info is null");
    PluginRender *render = GetPluginRender(env, info);
    if (render != nullptr) {
        int32_t ret = Player::GetInstance().Pause();
        if (ret != AVCODEC_SAMPLE_ERR_OK) {
            AVCODEC_SAMPLE_LOGI("Player pause failed.");
        }
    }
    return nullptr;
}

napi_value PluginRender::ResumePlay(napi_env env, napi_callback_info info) {
    CHECK_AND_RETURN_RET_LOG(env != nullptr && info != nullptr, nullptr, "env or info is null");
    PluginRender *render = GetPluginRender(env, info);
    if (render != nullptr) {
        int32_t ret = Player::GetInstance().Resume();
        if (ret != AVCODEC_SAMPLE_ERR_OK) {
            AVCODEC_SAMPLE_LOGI("Player pause failed.");
        }
    }
    return nullptr;
}

napi_value PluginRender::Seek(napi_env env, napi_callback_info info) {
    CHECK_AND_RETURN_RET_LOG(env != nullptr && info != nullptr, nullptr, "env or info is null");
    PluginRender *render = GetPluginRender(env, info);
    if (render != nullptr) {
        int64_t currentTime = 0;
        size_t argc = 1;
        napi_value args[1] = {0};
        napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
        napi_get_value_int64(env, args[0], &currentTime);
    
        int32_t ret = Player::GetInstance().Seek(currentTime);
        if (ret != AVCODEC_SAMPLE_ERR_OK) {
            OH_LOG_ERROR(LOG_APP, "PlayerSample video seek failed");
        }
    }
    return nullptr;
}

napi_value PluginRender::LoopPlay(napi_env env, napi_callback_info info) {
    CHECK_AND_RETURN_RET_LOG(env != nullptr && info != nullptr, nullptr, "env or info is null");
    PluginRender *render = GetPluginRender(env, info);
    if (render != nullptr) {
        bool isLoop = false;
        size_t argc = 1;
        napi_value args[1] = {nullptr};
        CHECK_AND_RETURN_RET_LOG(napi_ok == napi_get_cb_info(env, info, &argc, args, nullptr, nullptr), nullptr,
                                 "GetContext napi_get_cb_info failed");
        CHECK_AND_RETURN_RET_LOG(napi_ok == napi_get_value_bool(env, args[0], &isLoop), nullptr,
                                 "InitRecorder: Get value failed");
        int32_t ret = Player::GetInstance().Loop(isLoop);
        if (ret != AVCODEC_SAMPLE_ERR_OK) {
            OH_LOG_ERROR(LOG_APP, "Player loop failed");
        }
    }
    return nullptr;
}

napi_value PluginRender::InitPlayer(napi_env env, napi_callback_info info) {
    CHECK_AND_RETURN_RET_LOG(env != nullptr && info != nullptr, nullptr, "env or info is null");
    PluginRender *render = GetPluginRender(env, info);
    if (render != nullptr) {
        if (render->renderThread_->GetNativeImageWindow() != nullptr) {
            render->renderThread_->SetRenderAngle(false);
            size_t argc = 3;
            napi_value args[3] = {nullptr};
            CHECK_AND_RETURN_RET_LOG(napi_ok == napi_get_cb_info(env, info, &argc, args, nullptr, nullptr), nullptr,
                                     "GetContext napi_get_cb_info failed");
            SampleInfo sampleInfo;
            int index = 0;
            CHECK_AND_RETURN_RET_LOG(napi_ok == napi_get_value_int32(env, args[index++], &sampleInfo.inputFd), nullptr,
                                     "Initialize: Get value failed");
            CHECK_AND_RETURN_RET_LOG(napi_ok == napi_get_value_int64(env, args[index++], &sampleInfo.inputFileSize),
                                     nullptr, "Initialize: Get value failed");
            CHECK_AND_RETURN_RET_LOG(napi_ok == napi_get_value_int64(env, args[index++], &sampleInfo.inputFileOffset),
                                     nullptr, "Initialize: Get value failed");

            sampleInfo.window = render->renderThread_->GetNativeImageWindow();
            napi_value promise;
            napi_deferred deferred;
            CHECK_AND_RETURN_RET_LOG(napi_ok == napi_create_promise(env, &deferred, &promise), nullptr,
                                     "Initialize: Create promise failed");
            AsyncCallbackInfo *asyncCallbackInfo = new AsyncCallbackInfo();
            asyncCallbackInfo->env = env;
            asyncCallbackInfo->asyncWork = nullptr;
            asyncCallbackInfo->deferred = deferred;
            asyncCallbackInfo->sampleInfo = sampleInfo;
            napi_value resourceName;
            CHECK_AND_RETURN_RET_LOG(napi_ok ==
                                         napi_create_string_latin1(env, "Initialize", NAPI_AUTO_LENGTH, &resourceName),
                                     nullptr, "Initialize: Create resourceName failed");
            CHECK_AND_RETURN_RET_LOG(
                napi_ok == napi_create_async_work(
                               env, nullptr, resourceName,
                               [](napi_env env, void *data) { InitializePlayer(env, data); },
                               [](napi_env env, napi_status status, void *data) { DealCallBack(env, data); },
                               (void *)asyncCallbackInfo, &asyncCallbackInfo->asyncWork),
                nullptr, "Initialize: Create async work failed");
            CHECK_AND_RETURN_RET_LOG(napi_ok == napi_queue_async_work(env, asyncCallbackInfo->asyncWork), nullptr,
                                     "Initialize: queue_async_work failed");
            return promise;
        }
    }
    return nullptr;
}

void PluginRender::AddBW() { renderThread_->AddBW(); }

napi_value PluginRender::AddBW(napi_env env, napi_callback_info info) {
    PluginRender *render = GetPluginRender(env, info);
    if (render != nullptr) {
        render->AddBW();
    }
    return nullptr;
}

void PluginRender::OnSurfaceChanged(OH_NativeXComponent *component, void *window) {
    double offsetX;
    double offsetY;
    OH_NativeXComponent_GetXComponentOffset(component, window, &offsetX, &offsetY);
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OH_NativeXComponent_GetXComponentOffset",
                 "offsetX = %{public}lf, offsetY = %{public}lf", offsetX, offsetY);
    uint64_t width;
    uint64_t height;
    OH_NativeXComponent_GetXComponentSize(component, window, &width, &height);
    UpdateNativeWindow(window, width, height);
}

void PluginRender::OnTouchEvent(OH_NativeXComponent *component, void *window) {
    OH_NativeXComponent_TouchEvent touchEvent;
    OH_NativeXComponent_GetTouchEvent(component, window, &touchEvent);
    float tiltX = 0.0f;
    float tiltY = 0.0f;
    OH_NativeXComponent_TouchPointToolType toolType =
        OH_NativeXComponent_TouchPointToolType::OH_NATIVEXCOMPONENT_TOOL_TYPE_UNKNOWN;
    OH_NativeXComponent_GetTouchPointToolType(component, 0, &toolType);
    OH_NativeXComponent_GetTouchPointTiltX(component, 0, &tiltX);
    OH_NativeXComponent_GetTouchPointTiltY(component, 0, &tiltY);
    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OnTouchEvent",
                 "touch info: toolType = %{public}d, tiltX = %{public}lf, tiltY = %{public}lf", toolType, tiltX, tiltY);
}

void PluginRender::UpdateNativeWindow(void *window, uint64_t width, uint64_t height) {
    renderThread_->UpdateNativeWindow(window, width, height);
    // todo:等待renderThread把nativeImage创建成功，这里用这种方式并不是很合适
    while (renderThread_->GetNativeImageSurfaceId() == 0)
        ;
}

void PluginRender::RegisterCallback(OH_NativeXComponent *nativeXComponent) {
    renderCallback_.OnSurfaceCreated = OnSurfaceCreatedCB;
    renderCallback_.OnSurfaceChanged = OnSurfaceChangedCB;
    renderCallback_.OnSurfaceDestroyed = OnSurfaceDestroyedCB;
    renderCallback_.DispatchTouchEvent = DispatchTouchEventCB;
    OH_NativeXComponent_RegisterCallback(nativeXComponent, &renderCallback_);
}
} // namespace NativeXcomponentSample
